__asm__(".code16gcc");

#include "loader.h"

boot_info_t boot_info;			// 启动参数信息

uint16_t gdt_table[][4] = {
    {0,0,0,0},
    {0xFFFF, 0x0000, 0x9A00, 0x00CF},
    {0xFFFF, 0x0000, 0x9200, 0x00CF}

};

/**
 * BIOS下显示字符串
 */
static void show_msg(char *msg)
{
    char c;
    while ((c = *msg++) != '\0')
    {
        __asm__ __volatile__(
            "mov $0xe, %%ah\n\t"
            "mov %[ch], %%al\n\t"
            "int $0x10" ::[ch] "r"(c));
    }
}

/**
 * 检测内存
*/
static void detect_memory(void)
{
    uint32_t coutId = 0;
    smap_entry_t smap_entry;
    int signature, bytes;

    show_msg("try to dect_memory......\r\n");

    // 初次：EDX=0x534D4150,EAX=0xE820,ECX=24,INT 0x15, EBX=0（初次）
    // 后续：EAX=0xE820,ECX=24,
    // 结束判断：EBX=0
    boot_info.ram_region_count = 0;
    for (int i = 0; i < BOOT_RAM_REGION_MAX; i++)
    {
        smap_entry_t *entry = &smap_entry;

        __asm__ __volatile__("int $0x15"
                             : "=a"(signature), "=c"(bytes), "=b"(coutId)
                             : "a"(0xE820), "b"(coutId), "c"(24), "d"(0x534D4150),
                               "D"(entry));

        if (signature != 0x534D4150)
        {
            show_msg("faild.\r\n");
            return;
        }

        if (bytes > 20 && (entry->acpi & 0x0001) == 0)
        {
            continue;
        }

        // 保存RAM信息，只取32位，空间有限无需考虑更大容量的情况
        if (entry->type == 1)
        {
            boot_info.ram_region_cfg[boot_info.ram_region_count].start = entry->baseL;
            boot_info.ram_region_cfg[boot_info.ram_region_count].size = entry->lengthL;
            boot_info.ram_region_count++;
        }

        if (coutId == 0)
        {
            break;
        }
    }
    show_msg("dect_memory ok.\r\n");
}

/**
 * 进入保护模式
 */
void enter_protect_mode()
{
    //关中断
    cli();

    //开启A20地址线
    uint8_t v = inb(0x92);
    outb(0x92, v | 0x2);

    // 加载GDT。
    lgdt((uint32_t)gdt_table, sizeof(gdt_table));

    // 打开CR0的保护模式位，进入保护模式
    uint32_t cr0 = read_cr0();
    write_cr0(cr0 | (1 << 0));


    // 长跳转进入到保护模式
    far_jump(8, (uint32_t)protect_mode_entry);

}

void loader_entry(void)
{
    show_msg(".......loading.....\r\n");
    detect_memory();
    enter_protect_mode();
    for (;;)
    {
    }
}